
package com.x52im.rbchat.logic.chat_root.sendlocation.utils;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.amap.api.location.AMapLocation;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.core.PoiItem;
import com.amap.api.services.geocoder.BusinessArea;
import com.amap.api.services.geocoder.RegeocodeAddress;
import com.amap.api.services.geocoder.RegeocodeResult;
import com.eva.android.BitmapHelper;
import com.eva.android.ToolKits;
import com.eva.android.widget.DataLoadingAsyncTask;
import com.x52im.rbchat.Const;
import com.x52im.rbchat.MyApplication;
import com.x52im.rainbowchat.http.logic.dto.RosterElementEntity;
import com.x52im.rbchat.logic.chat_root.sendlocation.impl.GetLocationPOIEntity;
import com.x52im.rbchat.network.http.HttpFileUploadHelper;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

/**
 * 数据转换类.
 * 本类的原始代码来自开源工程：https://github.com/xiaofuchen/WeixinLocation
 *
 * @author 原作者：XiaoFu, 优化和修订：JackJiang
 * @since 6.0
 */

public class LocationUtils
{
    private final static String TAG = LocationUtils.class.getSimpleName();

    /** 记录定位信息 */
    public static final String LOCATION_INFO = "locationInfo";
    /** 记录搜索信息 */
    public static final String SEARCH_INFO = "searchInfo";

    /**
     * 取出AndroidManifest.xml配置的高德web服务的apikey。
     * 注：目前此apikey仅用于收到的位置消息时，通过高德web服务提供的静态地图功能，显示对应位置的静态预览图，目前别无他用！
     *
     * 关于高德的web服务说明，请见：https://lbs.amap.com/api/webservice/summary
     *
     * @param c
     * @return 成功获取则返回配置值，否则返回null
     */
    public static String getGaodeWebServicesAPIKey(Context c)
    {
        return ToolKits.getApplicationMetaData(c, "com.x52im.rbchat.amap.web.apikey");
    }

    /**
     * 获得下载指定位置消息的预览图片2进制数据的完整http地址.
     * <p>
     * 形如：“http://192.168.0.190:8080/rainbowchat_pro/BinaryDownloader?action=location_d
     *      &user_uid=400007&file_name=ae48f32af4094439b513557824f2c04c.jpg”。
     *
     * @param context
     * @param file_name 要下载的图片文件名
     * @param needDump 是否需要转储：true表示需要转储，否则不需要. 转储是用于图片消息接收方在打开了该图片消息完整图后
     * 通知服务端将此图进行转储（转储的可能性有2种：直接删除掉、移到其它存储位置），转储的目的是防止大量用户的大量图片
     * 被读过后还存储在服务器上，加大了服务器的存储压力。<b>注意：</b><u>读取缩略图时无需转储！</u>
     * @return 完整的http文件下载地址
     */
    public static String getPreviewImageDownloadURL(Context context, String file_name, boolean needDump)
    {
        String fileURL = null;
        if(MyApplication.getInstance(context).getIMClientManager().getLocalUserInfo() != null)
            fileURL = Const.BBONERAY_DOWNLOAD_CONTROLLER_URL_ROOT
                    +"?action=location_d"
                    // 要下载图片的用户uid
                    +"&user_uid="+MyApplication.getInstance(context).getIMClientManager().getLocalUserInfo().getUser_uid()
                    +"&file_name="+file_name
                    +"&need_dump="+(needDump?"1":"0");
        return fileURL;
    }

    /**
     * 获得指定位置消息的预览图片的完整http地址（通过高德地图提供的静态地图服务实现）.
     * <p>
     * 形如：“https://restapi.amap.com/v3/staticmap?
     * location=120.546825,31.304756&zoom=14&scale=1&size=720*300&key=4fb238d0544f80f40fb3cd057d268a5f”。
     *
     * @param context
     * @return 完整的高德地图静态图片http地址
     */
    public static String getPreviewImageDownloadURL2(Context context, double longitude, double latitude)
    {
        String gaodeWebServicesAPIKey = LocationUtils.getGaodeWebServicesAPIKey(context);
        String statidMapPreviewURL = "https://restapi.amap.com/v3/staticmap?" +
                "location="+longitude+","+latitude+"&zoom=14&scale=2&size=240*100&key="+gaodeWebServicesAPIKey;
        return statidMapPreviewURL;
    }

    /**
     * 返回存储的位置预览截图的目录（结尾带反斜线）.
     *
     * @return 如果SDCard等正常则返回目标路径，否则返回null
     */
    public static String getLocationPreviewImgSavedDirHasSlash()
    {
        String dir = getLocationPreviewImgSavedDir();

        return dir ==  null? null : (dir + "/");
    }
    /**
     * 返回存储的位置预览截图的目录.
     *
     * @return 如果SDCard等正常则返回目标路径，否则返回null
     */
    public static String getLocationPreviewImgSavedDir()
    {
        String dir = null;
        File sysExternalStorageDirectory = com.x52im.rbchat.utils.ToolKits.getDefaultCacheDir();//Environment.getExternalStorageDirectory();
        if(sysExternalStorageDirectory != null && sysExternalStorageDirectory.exists())
        {
            dir = sysExternalStorageDirectory.getAbsolutePath()
//                    + Const.DIR_KCHAT_LOCATION_RELATIVE_DIR;
                    + Const.DIR_KCHAT_IMG_RELATIVE_DIR;
        }

        return dir;
    }

    /**
     * 生成一个地图预览截图的文件名。
     *
     * 注：此截图理论上不可能发生重复，所以只需要保证文件名的唯一性，而无需像文件消息那样用文件数据的md5码来命名，
     * 所以目前为了简单起见，就直接用UUID就行了，但如果你觉得有必要用此图文件的md5码命名（实际无必要），自行修改即可。
     *
     * @return
     */
    public static String generateLocationPreviewFileName()
    {
        String uuid = UUID.randomUUID().toString();
        if(uuid != null)
            uuid = uuid.replaceAll("-", "")+".jpg";
        return uuid;
    }

    /**
     * @param errorCode 详见：https://lbs.amap.com/api/android-sdk/guide/map-tools/error-code
     * @return
     */
    public static String getSearchResponseErrorCodeDesc(int errorCode)
    {
        String tips = "搜索结果错误码：" + errorCode;

        switch (errorCode){
            case 1001: tips += ", 开发者签名未通过";break;
            case 1002: tips += ", 用户Key不正确或过期";break;
            case 1003: tips += ", 没有权限使用相应的接口";break;
            case 1008: tips += ", MD5安全码未通过验证";break;
            case 1009: tips += ", 请求Key与绑定平台不符";break;
            case 1012: tips += ", 权限不足，服务请求被拒绝";break;
            case 1013: tips += ", 该Key被删除";break;
            case 1100: tips += ", 引擎服务响应错误";break;
            case 1101: tips += ", 引擎返回数据异常";break;
            case 1102: tips += ", 高德服务端请求链接超时";break;
            case 1103: tips += ", 读取服务结果返回超时";break;
            case 1200: tips += ", 请求参数非法";break;
            case 1201: tips += ", 请求条件中，缺少必填参数";break;
            case 1202: tips += ", 服务请求协议非法";break;
            case 1203: tips += ", 服务端未知错误";break;
            case 1800: tips += ", 服务端新增错误";break;
            case 1801: tips += ", 协议解析错误";break;
            case 1802: tips += ", socket 连接超时 - SocketTimeoutException";break;
            case 1803: tips += ", url异常 - MalformedURLException";break;
            case 1804: tips += ", 未知主机 - UnKnowHostException";break;
            case 1806: tips += ", http或socket连接失败 - ConnectionException";break;
            case 1900: tips += ", 未知错误";break;
            case 1901: tips += ", 参数无效";break;
            case 1902: tips += ", IO 操作异常 - IOException";break;
            case 1903: tips += ", 空指针异常 - NullPointException";break;
            case 2000: tips += ", Tableid格式不正确";break;
            case 2001: tips += ", 数据ID不存在";break;
            case 2002: tips += ", 云检索服务器维护中";break;
            case 2003: tips += ", Key对应的tableID不存在";break;
            case 2100: tips += ", 找不到对应的userid信息";break;
            case 2101: tips += ", App Key未开通“附近”功能";break;
            case 2200: tips += ", 在开启自动上传功能的同时对表进行清除或者开启单点上传的功能";break;
            case 2201: tips += ", USERID非法";break;
            case 2202: tips += ", NearbyInfo对象为空";break;
            case 2203: tips += ", 两次单次上传的间隔低于7秒";break;
            case 2204: tips += ", Point为空，或与前次上传的相同";break;
            case 3000: tips += ", 规划点（包括起点、终点、途经点）不在中国陆地范围内";break;
            case 3001: tips += ", 规划点（包括起点、终点、途经点）附近搜不到路";break;
            case 3002: tips += ", 路线计算失败，通常是由于道路连通关系导致";break;
            case 3003: tips += ", 步行算路起点、终点距离过长导致算路失败。";break;
            case 4000: tips += ", 短串分享认证失败";break;
            case 4001: tips += ", 短串请求失败";break;
        }

        return tips;
    }

    public static String getLocationErrorDesc(int errorCode)
    {
        String tips = "定位错误码：" + errorCode;
        switch (errorCode) {
            case AMapLocation.ERROR_CODE_FAILURE_CONNECTION://4
                tips = "请检查设备网络是否通畅，检查通过接口设置的网络访问超时时间，建议采用默认的30秒。";
                break;
            case AMapLocation.ERROR_CODE_FAILURE_AUTH://7
                tips = "请仔细检查key绑定的sha1值与apk签名sha1值是否对应。";
                break;
            case AMapLocation.ERROR_CODE_FAILURE_LOCATION_PERMISSION://12
                tips = "请在设备的设置中开启app的定位权限。";
                break;
            case AMapLocation.ERROR_CODE_AIRPLANEMODE_WIFIOFF://18
                tips = "建议手机关闭飞行模式，并打开WIFI开关";
                break;
            case AMapLocation.ERROR_CODE_NOCGI_WIFIOFF://19
                tips = "建议手机插上sim卡，打开WIFI开关";
                break;
        }
        return tips;
    }

    public static void showLocationError(Activity parentActivity, int errorCode)
    {
        String tips = getLocationErrorDesc(errorCode);
        Toast.makeText(parentActivity.getApplicationContext(), tips, Toast.LENGTH_LONG).show();
    }

    public static AMapLocation changeToAMapLocation(PoiItem poiItem) {
        if (null != poiItem) {
            try {
                AMapLocation aMapLocation = new AMapLocation("lbs");
                aMapLocation.setAdCode(poiItem.getAdCode());
                aMapLocation.setAddress(poiItem.getProvinceName() + poiItem.getCityName() + poiItem.getAdName() + poiItem.getSnippet());
                aMapLocation.setCity(poiItem.getCityName());
                aMapLocation.setCityCode(poiItem.getCityCode());
                aMapLocation.setDistrict(poiItem.getAdName());
                aMapLocation.setLatitude(poiItem.getLatLonPoint().getLatitude());
                aMapLocation.setLongitude(poiItem.getLatLonPoint().getLongitude());
                aMapLocation.setPoiName(poiItem.getTitle());
                aMapLocation.setProvince(poiItem.getProvinceName());
                aMapLocation.setStreet(poiItem.getBusinessArea());
                return aMapLocation;
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
        return null;
    }

    public static GetLocationPOIEntity changeToPoiItem(AMapLocation data) {
        if (null != data) {
            try {
                String title = data.getDescription();
                if (TextUtils.isEmpty(title)) {
                    title = data.getPoiName();
                }
                if (TextUtils.isEmpty(title)) {
                    title = data.getStreet();
                }
                if (TextUtils.isEmpty(title)) {
                    title = "位置";
                }

//                Log.d(TAG, "!!!C, AMapLocation.toString()>>> "+data.toString());

                GetLocationPOIEntity poiItem = new GetLocationPOIEntity(data.getBuildingId()
                        , new LatLonPoint(data.getLatitude(), data.getLongitude()), title, data.getAddress());

                poiItem.setAdCode(data.getAdCode());
                poiItem.setAdName(data.getDistrict());
                poiItem.setBusinessArea(data.getStreet());
                poiItem.setCityCode(data.getCityCode());
                poiItem.setCityName(data.getCity());
                poiItem.setProvinceName(data.getProvince());

                poiItem.setContentForShow(data.getAddress());

                return poiItem;
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
        return null;
    }

    public static GetLocationPOIEntity changeToPoiItem(RegeocodeResult data) {
        if (null != data) {
            try {
                RegeocodeAddress ra = data.getRegeocodeAddress();

                String title = ra.getBuilding();
                if (TextUtils.isEmpty(title)) {
                    title = ra.getNeighborhood();
                }
                if (TextUtils.isEmpty(title)) {
                    title = ra.getTownship();
                }
                if (TextUtils.isEmpty(title)) {
                    title = ra.getFormatAddress();
                }
                if (TextUtils.isEmpty(title)) {
                    title = "位置";
                }

//                Log.d(TAG, "!!!【B】>>> getAdCode="+ra.getAdCode()+",getBuilding="+ra.getBuilding()+",getCity="+ra.getCity()
//                        +",getCityCode="+ra.getCityCode()+",getCountry="+ra.getCountry()+", getDistrict="+ra.getDistrict()
//                        +", getFormatAddress="+ra.getFormatAddress()+",getNeighborhood="+ra.getNeighborhood()+",getProvince="+ra.getProvince()
//                        +",getTowncode="+ra.getTowncode()+",getTownship="+ra.getTownship()+",getAois="+ra.getAois()
//                        +",getBusinessAreas="+ra.getBusinessAreas()+",getCrossroads="+ra.getCrossroads()+",getPois="+ra.getPois()
//                        +",getRoads="+ra.getRoads()+","+ra.getStreetNumber());

                GetLocationPOIEntity poiItem = new GetLocationPOIEntity(ra.getBuilding()
                        , data.getRegeocodeQuery().getPoint(), title, ra.getFormatAddress());

                poiItem.setAdCode(ra.getAdCode());
                poiItem.setAdName(ra.getDistrict());

                List<BusinessArea> ba = ra.getBusinessAreas();
                poiItem.setBusinessArea(ba.size()>0?ba.get(0).getName():null);

                poiItem.setCityCode(ra.getCityCode());
                poiItem.setCityName(ra.getCity());
                poiItem.setProvinceName(ra.getProvince());

                poiItem.setContentForShow(ra.getFormatAddress());

                return poiItem;
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
        return null;
    }

    /**
     * 保存位置预览图片到本地文件。
     *
     * @param bitmap 见 http://a.amap.com/lbs/static/unzip/Android_Map_Doc/3D/com/amap/api/maps/AMap.OnMapScreenShotListener.html
     * @param status 地图渲染状态， 1：地图渲染完成、0：未渲染完成
     * @param locationTitle 位置的描述，本参数可为空
     * @return 返回一个数据，Object[]{元素0：是否保存成功, 元素1：指向文件信息的File对象}
     */
    public static Object[] saveMapScreenShot(Bitmap bitmap, int status, String locationTitle, String fileSavedName)
    {
        boolean sucess = false;
        File imgFile = null;

        if(null == bitmap){
            Log.w(TAG, "【位置截图-保存】bitmap为空，没有成功截到位置预览图！");
            return new Object[]{false, null};
        }

        try
        {
            // 预览图的文件名（本地保存的名）
            String fileName = fileSavedName;//LocationUtils.generateLocationPreviewFileName();
            if(fileName != null)
            {
                Log.i(TAG, "【位置截图-保存】要保存的文件名为："+fileName+"，预览图保存进入下一步.....");

                // 将首帧图片进行尺寸压缩（太大的话，界面上看起来很丑）
                Bitmap afterOptimized = BitmapHelper.decreaseBitmapSize(bitmap
                        // 如果你觉得你不在意截出的原图太大，可以适当调整此压缩参数哦
                        , 1.0f, true);// 经过测试：1.0f+100%质量图大小约为650KB、0.6f+100%质量图大小约为303KB、、1.0f+75%质量图大小约为115KB

                // 如果尺寸压缩成功则继续处理
                if (afterOptimized != null)
                {
                    // 预览图将要保存的完整路径
                    imgFile = new File(LocationUtils.getLocationPreviewImgSavedDirHasSlash() + fileName);

                    // 如果要保存的目录不存在，则先创建之
                    if (!imgFile.getParentFile().exists())
                        imgFile.getParentFile().mkdirs();

                    // 将预览图保存到文件（图片保存质量设为75左右时，在图片品质几乎不变的情况下，文件大小可减小为原先的5分之1）
                    boolean imgSaveSucess = BitmapHelper.saveBitmapToFile(afterOptimized, 30, imgFile);

                    String statusDesc;
                    if (status != 0)
                        statusDesc = "地图渲染完成|截屏无网格";
                    else
                        statusDesc = "地图未渲染完成|截屏有网格";

                    if (imgSaveSucess) {
                        Log.i(TAG, "【位置截图-保存】预览图保存文件成功【OK】（位置：" + imgFile.getAbsolutePath() + "，" + statusDesc + "）");
                        sucess = true;
                    }
                }
                else
                {
                    Log.w(TAG, "【位置截图-保存】预览图尺寸压缩失败了，结果为null，预览图保存处理提前结束！");
                }
            }
            else
            {
                Log.w(TAG, "【位置截图-保存】要保存的fileName为空，位置预览图无法成功保存哦！");
            }
        }
        catch (OutOfMemoryError oom)
        {
            Log.w(TAG, "【位置截图-保存】位置"+locationTitle+"的预览图尺寸压缩时OOM了", oom);
        }
        catch (Exception e) {
            Log.w(TAG, "【位置截图-保存】位置"+locationTitle+"的预览图保存过程中出错了，原因："+e.getMessage(), e);
        }

        return new Object[]{sucess, imgFile};
    }


    /**
     * 位置预览图片文件上传异步实现类。
     *
     * 本类在调用时，需要传入的参数为：LocationPreviewUploaderAsync.excute(文件完整路径, 文件名).
     */
    public static class LocationPreviewUploaderAsync extends DataLoadingAsyncTask<Object, Integer, Boolean>
    {
        private String locationPreviewFilePath;
        private String locationPreviewFileName;

        /**
         * 构造方法。
         *
         * @param context 为了后台执行，建议此处传入Appliocation实例引用
         */
        public LocationPreviewUploaderAsync(Context context)
        {
            super(context, false);
        }

        @Override
        protected Boolean doInBackground(Object... objects)
        {
            locationPreviewFilePath = (String)objects[0];
            locationPreviewFileName = (String)objects[1];
            Boolean result = Boolean.FALSE;

            // 本地用户信息
            RosterElementEntity localUser = MyApplication.getInstance2().getIMClientManager().getLocalUserInfo();

            // 将本地用户的uid作为参数传递到服务端
            HashMap<String, String> requestProperties = new HashMap<String, String>();
            // 此参数名注意要与服务端保持一致哦
            requestProperties.put("user_uid", localUser != null?localUser.getUser_uid():"");
            requestProperties.put("file_name", locationPreviewFileName);

            // 开始上传预览图
            result = Boolean.valueOf(HttpFileUploadHelper.uploadFile(
                    locationPreviewFilePath
                    , locationPreviewFileName
                    , Const.LOCATION_PREVIEW_UPLOADER_CONTROLLER_URL_ROOT
                    , requestProperties));

            return result;
        }

        @Override
        protected void onPostExecuteImpl(Object _result)
        {
            if(_result != null)
            {
                Boolean result = (Boolean)_result;

                // 上传成功
                if(result)
                {
                    Log.i(TAG, "【位置截图-上传】预览图文件上传成功【OK】（"+locationPreviewFilePath+"）");
                }
                else
                {
                    Log.w(TAG, "【位置截图-上传】预览图文件上传失败【NO】（"+locationPreviewFilePath+"）");
                }
            }
        }
    }
}
